#include <math.h>
#include "stdc.h"
#include "color_space.h"

static double HUE2RGB(double v1, double v2, double vH) {
    if(vH < 0) { vH += 1; }
    if(vH > 1) { vH -= 1; }
    if(6.0 * vH < 1) { return v1 + (v2 - v1) * 6.0 * vH; }
    if(2.0 * vH < 1) { return v2; }
    if(3.0 * vH < 2) { return v1 + (v2 - v1) * ((2.0 / 3.0) - vH) * 6.0; }
    return (v1);
}

void HSL2RGB(double H, double S, double L, uint8_t *R, uint8_t *G, uint8_t *B) {
	if(!R || !G || !B) { return; }

    if(S == 0) {
        *R = 255.0 * L;
        *G = 255.0 * L;
        *B = 255.0 * L;
    } else {
		double v2 = (L < 0.5) ? (L * (1 + S)) : ((L + S) - (S * L));
        double v1 = 2.0 * L - v2;

        *R = 255.0 * HUE2RGB(v1, v2, H + (1.0 / 3.0));
        *G = 255.0 * HUE2RGB(v1, v2, H);
        *B = 255.0 * HUE2RGB(v1, v2, H - (1.0 / 3.0));
    }
}

void RGB2HSL(uint8_t red, uint8_t green, uint8_t blue, double *H, double *S, double *L) {
	if(!H || !S || !L) { return; }

	double R, G, B, Max, Min, dR, dG, dB, delta;

	// RGB 整数转换为比例
	R = red   / 255.0;
	G = green / 255.0;
	B = blue  / 255.0;

	Min = MIN3(R, G, B);    	// Min  value of RGB
	Max = MAX3(R, G, B);    	// Max. value of RGB
	delta = Max - Min;        	// Delta RGB value

	// 计算亮度
	*L = (Max + Min) / 2.0;

	if(delta == 0) {
		// 各颜色相等，为灰色
		// *H = 2.0/3.0;        // Windows env S=0, H=160(2/3*240)
		*H = 0;                 // HSL results = 0 / 1
		*S = 0;
	} else {
		// 彩色 Chromatic data
		// 计算饱和度
		*S = (*L < 0.5) ? (delta / (Max + Min)) : (delta / (2 - Max - Min));

		// 计算色相
		dR = (((Max - R) / 6.0) + (delta / 2.0)) / delta;
		dG = (((Max - G) / 6.0) + (delta / 2.0)) / delta;
		dB = (((Max - B) / 6.0) + (delta / 2.0)) / delta;

		/**/ if (R == Max) { *H = dB - dG; }
		else if (G == Max) { *H = (1.0 / 3.0) + dR - dB; }
		else if (B == Max) { *H = (2.0 / 3.0) + dG - dR; }

		if(*H < 0) { *H += 1; }
		if(*H > 1) { *H -= 1; }
	}
}


static uint8_t double_to_u8(double val) {	
	if(val < 0)   { val = 0; }
	if(val > 255) { val = 255; }
	return (uint8_t)val;
}

void T2RGB(uint16_t T, uint8_t *R, uint8_t *G, uint8_t *B) {
	double tmpCalc;
	double tmpKelvin = T;
	
	// Temperature must fall between 1000 and 40000 degrees
	if(tmpKelvin < 1000)  { tmpKelvin = 1000; }
	if(tmpKelvin > 40000) { tmpKelvin = 40000; }
	
	// All calculations require tmpKelvin / 100, so only do the conversion once
    tmpKelvin = tmpKelvin / 100;
	
    // Calculate each color in turn
    
    // First: red
    if(tmpKelvin <= 66) {
        tmpCalc = 255;
    } else {
        // Note: the R-squared value for this approximation is .988
        tmpCalc = tmpKelvin - 60;
        tmpCalc = 329.698727446 * pow(tmpCalc, -0.1332047592);
    }
	*R = double_to_u8(tmpCalc);
    
    // Second: green
    if(tmpKelvin <= 66) {
        // Note: the R-squared value for this approximation is .996
        tmpCalc = tmpKelvin;
        tmpCalc = 99.4708025861 * log(tmpCalc) - 161.1195681661;
    } else {
        // Note: the R-squared value for this approximation is .987
        tmpCalc = tmpKelvin - 60;
        tmpCalc = 288.1221695283 * pow(tmpCalc, -0.0755148492);
    }
	*G = double_to_u8(tmpCalc);
    
    // Third: blue
    if(tmpKelvin >= 66) {
        tmpCalc = 255;
    } else if(tmpKelvin <= 19) {
        tmpCalc = 0;
    } else {
        // Note: the R-squared value for this approximation is .998
        tmpCalc = tmpKelvin - 10;
        tmpCalc = 138.5177312231 * log(tmpCalc) - 305.0447927307;
    }
	*B = double_to_u8(tmpCalc);
}

void RGB2T(uint8_t R, uint8_t G, uint8_t B, uint16_t *T) {
	// TODO 暂未实现
}
